package juego;

import java.util.*;
import movimiento.Posicion;
import objetos.*;
import objetos.aeronaves.*;
import objetos.aeronaves.civiles.*;
import objetos.aeronaves.enemigos.*;
import objetos.powerUps.*;
import objetos.proyectiles.*;
import org.jdom.*;
import programa.Partida;
import ar.uba.fi.algo3.titiritero.*;
import ar.uba.fi.algo3.titiritero.vista.Imagen;

/*
 * Clase que contiene a todos los objetos m�viles en un nivel. Tiene listas con todos
 * los objetos m�viles en juego y en reserva, y metodos para operar con �llos.
 */
public class Escenario implements Posicionable {
	/* Implements Singleton */
	private static Escenario instancia;
	/* Referencia al controlador de juego que esta manejando el escenario */
	private ControladorJuego controlador;
	/* El escenario tiene referencias a los objetosMoviles */
	private ArrayList<Enemigo> enemigos;
	private ArrayList<Civil> civiles;
	private ArrayList<Proyectil> proyectilesEnemigos, proyectilesDelAlgo42;
	private ArrayList<PowerUp> powerUps;
	private ArrayList<ObjetoNoControlado> aeronavesEnReserva;
	private Algo42 actor;
	private AvionGuia avionGuia;
	private boolean hayAvionGuia;
	private int topePuntaje;
	private Nivel nivelActual;
	private int numeroNivel;
	private int cantEnemigosEnReserva = 0;
	/* Constantes de clase que indican el tamanio del escenario */
	private int maxX = 720;
	private int maxY = 700;
	/* Numeros para informar al final del escenario */
	private int enemigosDestruidos = 00;
	private int civilesDestruidos = 00;

	private Escenario() {
		/* Crea un nivel vacio */
		this.enemigos = new ArrayList<Enemigo>();
		this.civiles = new ArrayList<Civil>();
		this.proyectilesDelAlgo42 = new ArrayList<Proyectil>();
		this.proyectilesEnemigos = new ArrayList<Proyectil>();
		this.powerUps = new ArrayList<PowerUp>();
		this.aeronavesEnReserva = new ArrayList<ObjetoNoControlado>();
		this.actor = new Algo42();
		this.hayAvionGuia = false;
		this.avionGuia = null;
	}

	public static Escenario getInstance() {
		if (instancia == null) {
			instancia = new Escenario();
		}
		return instancia;
	}

	public static void resetear() {
		instancia = new Escenario();
	}

	public void finNivel() {
		this.controlador.detenerJuego();
		boolean gano = !Escenario.getInstance().getActor().estaDestruido();
		Partida.getInstance().finDeNivel(gano);
	}

	public void agregarObjeto(Enemigo enemigo) {
		this.enemigos.add(enemigo);

		Imagen vista = enemigo.darVista(this.controlador);
		this.controlador.agregarDibujable(vista);
	}

	public void agregarObjeto(Civil civil) {
		this.civiles.add(civil);

		Imagen vista = civil.darVista(this.controlador);
		this.controlador.agregarDibujable(vista);
	}

	public void agregarObjeto(AvionGuia avionGuia) {
		this.avionGuia = avionGuia;
		this.controlador.agregarDibujable(avionGuia.darVista(this.controlador));
	}

	public void agregarObjeto(Proyectil bala) {
		if (bala.esEnemigo()) {
			this.proyectilesEnemigos.add(bala);
		} else {
			this.proyectilesDelAlgo42.add(bala);
		}

		Imagen vista = bala.darVista(this.controlador);
		this.controlador.agregarDibujable(vista);
	}

	public void agregarObjeto(PowerUp powerUp) {
		this.powerUps.add(powerUp);

		Imagen vista = powerUp.darVista(this.controlador);
		this.controlador.agregarDibujable(vista);
	}

	public ObjetoNoControlado getAeronaveEnReserva() {
		/* Devuelve y quita una aeronave de la lista de aeronaves en reserva */
		Random rdm = new Random();
		int index = rdm.nextInt(this.aeronavesEnReserva.size());
		ObjetoNoControlado avion = this.aeronavesEnReserva.get(index);
		aeronavesEnReserva.remove(index);
		return avion;
	}

	public void ponerAeronaveEnReserva(ObjetoNoControlado aeronave) {
		aeronave.setPosicion(new Posicion(0, 0));
		this.aeronavesEnReserva.add(aeronave);
	}

	public ObjetoMovil darObjetivoPara(ObjetoMovil base) {
		/*
		 * Usado para los misiles rastreadores. Devuelve un objetivo para un
		 * misil disparado por "base"
		 */
		if (base.esEnemigo()) {
			return this.actor;
		}
		return this.obtenerEnemigo();
	}

	public Enemigo obtenerEnemigo() {
		/* Devuelve un enemigo al azar de la lista de enemigos */
		if (this.hayAvionGuia && this.avionGuia != null) {
			return this.avionGuia;
		}
		Random rdm = new Random();
		Enemigo objetivo = null;
		if (this.enemigos.size() != 0) {
			while (objetivo == null) {
				objetivo = this.enemigos.get(rdm.nextInt(this.enemigos.size()));
			}
		}
		return objetivo;
	}

	public Algo42 getActor() {
		return actor;
	}

	public void setActor(Algo42 actor) {
		this.actor = actor;

		Imagen vista = actor.darVista(this.controlador);
		this.controlador.agregarDibujable(vista);
	}

	public int getNumeroNivel() {
		return numeroNivel;
	}

	public void setNumeroNivel(int numeroNivel) {
		this.numeroNivel = numeroNivel;
	}

	public Nivel getNivel() {
		return nivelActual;
	}

	public void setNivel(Nivel nivel) {
		this.nivelActual = nivel;
	}

	public int getMaxX() {
		return maxX;
	}

	public void setMaxX(int max) {
		maxX = max;
	}

	public int getMaxY() {
		return maxY;
	}

	public void setMaxY(int max) {
		maxY = max;
	}

	public void setTopePuntaje(int topePuntaje) {
		this.topePuntaje = topePuntaje;
	}

	public int getTopePuntaje() {
		return topePuntaje;
	}

	public void setCantEnemigosEnReserva(int cantEnemigosEnReserva) {
		this.cantEnemigosEnReserva = cantEnemigosEnReserva;
	}

	public int getCantEnemigosEnReserva() {
		return cantEnemigosEnReserva;
	}

	public ArrayList<Enemigo> getEnemigos() {
		return enemigos;
	}

	public ArrayList<Civil> getCiviles() {
		return civiles;
	}

	public ArrayList<Proyectil> getProyectilesEnemigos() {
		return proyectilesEnemigos;
	}

	public ArrayList<Proyectil> getProyectilesDelAlgo42() {
		return proyectilesDelAlgo42;
	}

	public ArrayList<PowerUp> getPowerUps() {
		return powerUps;
	}

	public ArrayList<ObjetoNoControlado> getAeronavesEnReserva() {
		return aeronavesEnReserva;
	}

	public void setHayAvionGuia(boolean hayAvionGuia) {
		this.hayAvionGuia = hayAvionGuia;
	}

	public boolean getHayAvionGuia() {
		return hayAvionGuia;
	}

	public AvionGuia getAvionGuia() {
		return avionGuia;
	}

	public ControladorJuego getControlador() {
		return controlador;
	}

	public void setControlador(ControladorJuego controlador) {
		this.controlador = controlador;
	}

	public void destruiUnEnemigo() {
		this.enemigosDestruidos++;
	}

	public void destruiUnCivil() {
		this.civilesDestruidos++;
	}

	@Override
	public int getX() {
		return 0;
	}

	@Override
	public int getY() {
		return 0;
	}

	public int getEnemigosDestruidos() {
		return enemigosDestruidos;
	}

	public void setEnemigosDestruidos(int enemigosDestruidos) {
		this.enemigosDestruidos = enemigosDestruidos;
	}

	public int getCivilesDestruidos() {
		return civilesDestruidos;
	}

	public void setCivilesDestruidos(int civilesDestruidos) {
		this.civilesDestruidos = civilesDestruidos;
	}

	/* Persistencia Instancia a partir de NodoXML */
	public void cargarEscenario(Element nodo) {
		Escenario.getInstance().setNumeroNivel(
				Integer.parseInt(nodo.getAttributeValue("nivel")));
		Escenario.getInstance().setTopePuntaje(
				Integer.parseInt(nodo.getAttributeValue("topePuntaje")));
		Escenario.getInstance().setEnemigosDestruidos(
				Integer.parseInt(nodo.getAttributeValue("enemigosDestruidos")));
		Escenario.getInstance().setCivilesDestruidos(
				Integer.parseInt(nodo.getAttributeValue("civilesDestruidos")));

		if (Boolean.valueOf(nodo.getAttributeValue("hayGuia"))) {
			Escenario.getInstance().agregarObjeto(
					new AvionGuia(nodo.getChild("avionGuia")));
		}

		/* Agrego enemigos */
		ArrayList<Enemigo> enemigosPorSerAgregados = this.crearEnemigos(nodo
				.getChild("enemigos"));
		if (enemigosPorSerAgregados.isEmpty()) {
			System.out.println("HEY ESTA VACA");
		}
		for (Enemigo enemigo : enemigosPorSerAgregados) {
			Escenario.getInstance().agregarObjeto(enemigo);
		}

		/* Agrego civiles */
		ArrayList<Civil> civilesPorSerAgregados = this.crearCiviles(nodo
				.getChild("civiles"));

		for (Civil civil : civilesPorSerAgregados) {
			Escenario.getInstance().agregarObjeto(civil);
		}

		/* Agrego proyectilesAliados */
		this.crearProyectiles(nodo.getChild("proyectilesAliados"), "aliado",
				this.getActor());

		/* Agrego proyectilesEnemigos */
		this.crearProyectiles(nodo.getChild("proyectilesEnemigos"), "enemigo",
				this.obtenerEnemigo());

		/* Agrego los powerUps */
		this.agregarPowerUps(nodo.getChild("powerUps"));

	}

	/* NodoXML a partir de instancia */
	public Element obtenerNodo() {
		Element nodo = new Element("escenario");
		nodo.setAttribute(new Attribute("nivel", String
				.valueOf(this.numeroNivel)));
		nodo.setAttribute(new Attribute("hayGuia", String
				.valueOf(this.avionGuia != null)));
		nodo.setAttribute(new Attribute("topePuntaje", String
				.valueOf(this.topePuntaje)));
		nodo.setAttribute(new Attribute("enemigosDestruidos", String
				.valueOf(this.enemigosDestruidos)));
		nodo.setAttribute(new Attribute("civilesDestruidos", String
				.valueOf(this.civilesDestruidos)));
		/* Si haya avion guia, lo agrego */
		if (avionGuia != null) {
			nodo.addContent(this.avionGuia.obtenerNodo());
		}

		/* Tag para enemigos */
		Element tagEnemigos = new Element("enemigos");

		for (Enemigo enemy : this.enemigos) {
			tagEnemigos.addContent(enemy.obtenerNodo());
		}
		nodo.addContent(tagEnemigos);

		/* Tag para civiles */
		Element tagCiviles = new Element("civiles");

		for (Civil civil : this.civiles) {
			tagCiviles.addContent(civil.obtenerNodo());
		}
		nodo.addContent(tagCiviles);

		/* Tag para proyectiles aliados */
		Element tagBalasBuenas = new Element("proyectilesAliados");

		for (Proyectil bala : this.proyectilesDelAlgo42) {
			tagBalasBuenas.addContent(bala.obtenerNodo());
		}
		nodo.addContent(tagBalasBuenas);

		/* Tag para proyectiles Enemigos */
		Element tagBalasMalas = new Element("proyectilesEnemigos");

		for (Proyectil bala : this.proyectilesEnemigos) {
			tagBalasMalas.addContent(bala.obtenerNodo());
		}
		nodo.addContent(tagBalasMalas);

		/* Tag para powerUps */
		Element powerUps = new Element("powerUps");

		for (PowerUp powerUp : this.powerUps) {
			powerUps.addContent(powerUp.obtenerNodo());
		}
		nodo.addContent(powerUps);

		/* Tag para aeronaves en reserva */
		Element tagReserva = new Element("aeronavesEnReserva");

		for (ObjetoNoControlado avion : this.aeronavesEnReserva) {
			tagReserva.addContent(avion.obtenerNodo());
		}
		nodo.addContent(tagReserva);

		return nodo;
	}

	@SuppressWarnings("rawtypes")
	private ArrayList<Enemigo> crearEnemigos(Element enemigos) {
		ArrayList<Enemigo> enemigosPorSerAgregados = new ArrayList<Enemigo>();

		/* Agrego Cazas */
		Iterator iter = enemigos.getChildren("caza").iterator();

		while (iter.hasNext()) {
			Element nodoCaza = (Element) iter.next();
			enemigosPorSerAgregados.add(new Caza(nodoCaza));
		}

		/* Agrego Bombers */
		iter = enemigos.getChildren("bomber").iterator();

		while (iter.hasNext()) {
			Element nodoBomber = (Element) iter.next();
			enemigosPorSerAgregados.add(new Bomber(nodoBomber));
		}

		/* Agrego Avionetas */
		iter = enemigos.getChildren("avioneta").iterator();

		while (iter.hasNext()) {
			Element nodoAvioneta = (Element) iter.next();
			enemigosPorSerAgregados.add(new Avioneta(nodoAvioneta));
		}

		/* Agrego Scouts */
		iter = enemigos.getChildren("scout").iterator();

		while (iter.hasNext()) {
			Element nodoScout = (Element) iter.next();
			enemigosPorSerAgregados.add(new Scout(nodoScout));
		}

		return enemigosPorSerAgregados;
	}

	@SuppressWarnings("rawtypes")
	private ArrayList<Civil> crearCiviles(Element civiles) {
		ArrayList<Civil> civilesPorSerAgregados = new ArrayList<Civil>();

		/* Agrego los aviones civiles */
		Iterator iter = civiles.getChildren("avionCivil").iterator();

		while (iter.hasNext()) {
			Element nodoAvion = (Element) iter.next();
			civilesPorSerAgregados.add(new AvionCivil(nodoAvion));
		}

		/* Agrego Helicopteros */
		iter = civiles.getChildren("helicoptero").iterator();

		while (iter.hasNext()) {
			Element nodoHelicoptero = (Element) iter.next();
			civilesPorSerAgregados.add(new Helicoptero(nodoHelicoptero));
		}

		return civilesPorSerAgregados;

	}

	@SuppressWarnings("rawtypes")
	private void crearProyectiles(Element proyectiles, String bando,
			ObjetoMovil origen) {
		ArrayList<Proyectil> proyectilesPorSerAgregados = new ArrayList<Proyectil>();

		/* Agrego los lasers */
		Iterator iter = proyectiles.getChildren("laserShoot").iterator();

		while (iter.hasNext()) {
			Element nodo = (Element) iter.next();
			LaserShoot laser = new LaserShoot(nodo);
			laser.setBando(bando);
			laser.setOrigen(origen);
			proyectilesPorSerAgregados.add(laser);

		}

		/* Agrego los cohetes */
		iter = proyectiles.getChildren("cohete").iterator();

		while (iter.hasNext()) {
			Element nodo = (Element) iter.next();
			Cohete cohete = new Cohete(nodo);
			cohete.setBando(bando);
			cohete.setOrigen(origen);
			proyectilesPorSerAgregados.add(cohete);

		}

		/* Agrego los torpedos */
		iter = proyectiles.getChildren("torpedo").iterator();

		while (iter.hasNext()) {
			Element nodo = (Element) iter.next();
			Torpedo torpedo = new Torpedo(nodo);
			torpedo.setBando(bando);
			torpedo.setOrigen(origen);
			proyectilesPorSerAgregados.add(torpedo);

		}

		/* Agrego los rastreadores */
		iter = proyectiles.getChildren("rastreador").iterator();

		while (iter.hasNext()) {
			Element nodo = (Element) iter.next();
			Rastreador rastreador = new Rastreador(nodo);
			rastreador.setBando(bando);
			rastreador.setOrigen(origen);
			proyectilesPorSerAgregados.add(rastreador);

		}

		/* Agrego los adaptables */
		iter = proyectiles.getChildren("adaptable").iterator();

		while (iter.hasNext()) {
			Element nodo = (Element) iter.next();
			Adaptable adaptable = new Adaptable(nodo);
			adaptable.setBando(bando);
			adaptable.setOrigen(origen);
			proyectilesPorSerAgregados.add(adaptable);

		}

		for (Proyectil bala : proyectilesPorSerAgregados) {
			Escenario.getInstance().agregarObjeto(bala);
		}
	}

	@SuppressWarnings("rawtypes")
	private void agregarPowerUps(Element powerUps) {
		Iterator iter = powerUps.getChildren("deArma").iterator();

		while (iter.hasNext()) {
			@SuppressWarnings("unused")
			Element nodo = (Element) iter.next();
		}

		iter = powerUps.getChildren("deEnergia").iterator();

		while (iter.hasNext()) {
			Element nodo = (Element) iter.next();
			Escenario.getInstance().agregarObjeto(new DeEnergia(nodo));
		}

	}

}
